<!DOCTYPE html>  
<html>
	<meta charset="utf-8" />  
	<title>indexedDB</title>
	<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
	<style type="text/css">
		body {
		  font-size: 0.8em;
		  font-family: Sans-Serif;
		}

		form {
		  background-color: #cccccc;
		  border-radius: 0.3em;
		  display: inline-block;
		  margin-bottom: 0.5em;
		  padding: 1em;
		}

		table {
		  border-collapse: collapse;
		}

		input {
		  padding: 0.3em;
		  border-color: #cccccc;
		  border-radius: 0.3em;
		}

		.required:after {
		  content: "*";
		  color: red;
		}

		.button-pane {
		  margin-top: 1em;
		}

		#documents-list {
		  background-color: #eeeeee;
		}

		.action-success {
		  margin-left: 1em;
		  padding: 0.5em;
		  color: #00d21e;
		  background-color: #eeeeee;
		  border-radius: 0.2em;
		}

		.action-failure {
		  margin-left: 1em;
		  padding: 0.5em;
		  color: #ff1408;
		  background-color: #eeeeee;
		  border-radius: 0.2em;
		}

		.presence-no {
		  font-style: italic;
		}

		.note {
		  font-size: smaller;
		}
	</style>
<body>
	<h1>IndexedDB Demo: storing blobs, e-publications example</h1>
    <div id="msg">
    </div>

    <form id="register-form">
      <table>
        <tbody>
          <tr>
            <td>
              <label for="pub-title" class="required">
                Title:
              </label>
            </td>
            <td>
              <input type="text" id="pub-title" name="pub-title" />
            </td>
          </tr>
          <tr>
            <td>
              <label for="pub-year" class="required">
                Year:
              </label>
            </td>
            <td>
              <input type="number" id="pub-year" name="pub-year" />
            </td>
          </tr>
          <tr>
            <td>
              <label for="pub-biblioid" class="required">
                Bibliographic ID
                <span class="note">(ISBN, ISSN, etc.)</span>:
              </label>
            </td>
            <td>
              <input type="text" id="pub-biblioid" name="pub-biblioid"/>
            </td>
          </tr>
        </tbody>
        <tbody>
          <tr>
            <td>
              <label for="pub-file">
                File image:
              </label>
            </td>
            <td>
              <input type="file" id="pub-file" accept="image/*"/>
            </td>
          </tr>
          <tr>
            <td>
              <label for="pub-content-url">
                Online-file image URL:
              </label>
            </td>
            <td>
              <input type="text" id="pub-content-url" name="pub-content-url"/>
            </td>
          </tr>
        </tbody>
      </table>

      <div class="button-pane">
        <input type="button" id="add-button" value="Add Publication" />
        <input type="reset" id="register-form-reset"/>
        <span id="action-status"></span>
      </div>
    </form>

    <form id="delete-form">
      <div>
        <label for="pub-biblioid-to-delete" class="required">
          Key (for example 1, 2, 3, etc.):
        </label>
        <input type="text" id="pub-biblioid-to-delete"
               name="pub-biblioid-to-delete" />
      </div>
      <div class="button-pane">
        <input type="button" id="delete-button" value="Delete Publication" />
      </div>
    </form>

    <form id="search-form">
      <div class="button-pane">
        <input type="button" id="search-list-button"
               value="List database content" />
      </div>
    </form>

    <div>
      <div id="pub-msg">
      </div>
      <ul id="pub-list">
      </ul>
    </div>  
</body>
<script type="text/javascript">
	(function () {
	  // Works with:
	  // * Firefox >= 16.0
	  // * Google Chrome >= 24.0 (you may need to get Google Chrome Canary)

	  const DB_NAME = 'mdn-demo-indexeddb-epublications';
	  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
	  const DB_STORE_NAME = 'publications';

	  var db;

	  function initDb() {
	    console.debug("initDb ...");
	    var req = indexedDB.open(DB_NAME, DB_VERSION);
	    req.onsuccess = function (evt) {
	      // Better use "this" than "req" to get the result to avoid problems with
	      // garbage collection.
	      // db = req.result;
	      db = this.result;
	      console.debug("initDb DONE");
	    };
	    req.onerror = function (evt) {
	      console.error("initDb:", evt.target.errorCode);
	    };

	    req.onupgradeneeded = function (evt) {
	      console.debug("initDb.onupgradeneeded");
	      var store = evt.currentTarget.result.createObjectStore(
	        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });

	      store.createIndex('biblioid', 'biblioid', { unique: true });
	      store.createIndex('title', 'title', { unique: false });
	      store.createIndex('year', 'year', { unique: false });
	    };
	  }

	  function getFile(key, success_callback) {
	    var tx = db.transaction(DB_STORE_NAME, 'readonly');
	    var store = tx.objectStore(DB_STORE_NAME);
	    var req = store.get(key);
	    req.onsuccess = function(evt) {
	      var value = evt.target.result;
	      if (value)
	        success_callback(value.file);
	    };
	  }

	  function displayPubList() {
	    console.debug("displayPubList");
	    var pub_msg = $('#pub-msg');
	    pub_msg.empty();
	    var pub_list = $('#pub-list');
	    pub_list.empty();

	    var tx = db.transaction(DB_STORE_NAME, 'readonly');
	    var store = tx.objectStore(DB_STORE_NAME);
	    var req;

	    req = store.count();
	    // Requests are executed in the order in which they were made against the
	    // transaction, and their results are returned in the same order.
	    // Thus the count text below will be displayed before the actual pub list
	    // (not that it is algorithmically important in this case).
	    req.onsuccess = function(evt) {
	      pub_msg.append('<p>There are <strong>' + evt.target.result +
	                     '</strong> record(s) in the object store.</p>');
	    };
	    req.onerror = function(evt) {
	      console.error("add error", this.error);
	      displayActionFailure(this.error);
	    };

	    var i = 0;
	    var img_id;
	    var file_presence;
	    var presence_html;
	    req = store.openCursor();
	    req.onsuccess = function(evt) {
	      var cursor = evt.target.result;
	      if (cursor) {
	        presence_html = "<span class='presence-no'>No image</span>";
	        file_presence = cursor.value.file != null;
	        console.debug("cursor.value:", cursor.value);
	        if (file_presence) {
	          img_id = 'pub-img-' + i;
	          presence_html = '<img id="' + img_id + '"/>';
	          getFile(cursor.key, function(file) {
	            console.debug("file:", file);

	            // Note that here it is not possible to set a link to the file to
	            // make  it possible to download it.
	            // The only possible options are:
	            // * display the file if it is an image
	            // * getting text/other info from the file and display them
	            var obj_url = window.URL.createObjectURL(file);
	            $('#' + img_id).attr('src', obj_url);
	            window.URL.revokeObjectURL(obj_url);
	          });
	        }
	        pub_list.append('<li>' +
	                        '[' + cursor.key + '] ' +
	                        '(biblioid: ' + cursor.value.biblioid + ') ' +
	                        cursor.value.title + ' - ' +
	                        cursor.value.year + ' / ' +
	                        presence_html +
	                        '</li>');

	        // Move on to the next object in store
	        cursor.continue();

	        // This counter serves only to create distinct img ids
	        i++;
	      } else {
	        console.debug("No more entries!");
	      }
	    };
	  };

	  function addPublication(biblioid, title, year, file) {
	    console.debug("addPublication arguments:", arguments);
	    if (!db) {
	      console.error("addPublication: the db is not initialized");
	      return;
	    }
	    var tx = db.transaction(DB_STORE_NAME, 'readwrite');
	    var store = tx.objectStore(DB_STORE_NAME);
	    var req = store.add({ biblioid: biblioid, title: title, year: year,
	                          file: file });
	    req.onsuccess = function (evt) {
	      console.debug("Insertion in DB successful");
	      displayPubList();
	    };
	    req.onerror = function() {
	      console.error("add error", this.error);
	      displayActionFailure(this.error);
	    };
	  }

	  function displayActionSuccess(msg) {
	    msg = typeof msg !== 'undefined' ? "Success: " + msg : "Success";
	    $('#action-status').html('<span class="action-success">' + msg + '</span>');
	  }
	  function displayActionFailure(msg) {
	    msg = typeof msg !== 'undefined' ? "Failure: " + msg : "Failure";
	    $('#action-status').html('<span class="action-failure">' + msg + '</span>');
	  }
	  function resetActionStatus() {
	    console.debug("resetActionStatus ...");
	    $('#action-status').empty();
	    console.debug("resetActionStatus DONE");
	  }

	  function addEventListeners() {
	    console.debug("addEventListeners");
	    initDb();

	    $('#register-form-reset').click(function(evt) {
	      resetActionStatus();
	    });

	    $('#add-button').click(function(evt) {
	      console.debug("add ...");
	      var title = $('#pub-title').val();
	      var year = $('#pub-year').val();
	      var biblioid = $('#pub-biblioid').val();
	      if (!title || !year || !biblioid) {
	        displayActionFailure("Required field(s) missing");
	        return;
	      }

	      var file_input = $('#pub-file');
	      var selected_file = file_input.get(0).files[0];
	      console.debug("selected_file:", selected_file);
	      file_input.val(null);
	      var content_url = $('#pub-content-url').val();
	      if (selected_file) {
	        addPublication(biblioid, title, year, selected_file);
	      } else {
	        addPublication(biblioid, title, year);
	        displayActionSuccess();
	      }

	    });

	    $('#delete-button').click(function(evt) {
	      console.debug("delete ...");
	      var k = $('#pub-biblioid-to-delete').val();
	      console.debug("delete k:", k);
	      var tx = db.transaction(DB_STORE_NAME, 'readwrite');
	      var store = tx.objectStore(DB_STORE_NAME);

	      // Warning: The exact same key used for creation needs to be passed for
	      // the deletion. If the key was a Number for creation, then it needs to be
	      // a Number for deletion.
	      k = Number(k);

	      // The code that could be nice if it worked
	      // var req = store.delete(k);
	      // req.onsuccess = function(evt) {
	      //   var record = evt.target.result;
	      //   console.debug("record:", record);
	      //   if (typeof record !== 'undefined') {
	      //     displayActionSuccess("Deletion successful");
	      //     displayPubList();
	      //   } else {
	      //     displayActionFailure("No matching record found");
	      //   }
	      // };
	      // req.onerror = function (evt) {
	      //   console.error("delete:", evt.target.errorCode);
	      // };

	      // The code that actually works
	      //
	      // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
	      // the result of the Object Store Deletion Operation algorithm is
	      // undefined, so it's not possible to know if some records were actually
	      // deleted by looking at the request result.
	      var req = store.get(k);
	      req.onsuccess = function(evt) {
	        var record = evt.target.result;
	        console.debug("record:", record);
	        if (typeof record !== 'undefined') {
	          req = store.delete(k);
	          req.onsuccess = function(evt) {
	            console.debug("evt:", evt);
	            console.debug("evt.target:", evt.target);
	            console.debug("evt.target.result:", evt.target.result);
	            console.debug("delete successful");
	            displayActionSuccess("Deletion successful");
	            displayPubList();
	          };
	          req.onerror = function (evt) {
	            console.error("delete:", evt.target.errorCode);
	          };
	        } else {
	          displayActionFailure("No matching record found");
	        }
	      };
	      req.onerror = function (evt) {
	        console.error("delete:", evt.target.errorCode);
	      };

	    });

	    var search_button = $('#search-list-button');
	    search_button.click(function(evt) {
	      displayPubList();
	    });

	  }

	// function dbAddAndGetBlob(title, year, url) {
	//   // Create XHR
	//   var xhr = new XMLHttpRequest();
	//   var blob;

	//   // We can't use jQuery here because as of jQuery 1.7.2 the new "blob"
	//   // responseType is not handled.
	//   // http://bugs.jquery.com/ticket/7248
	//   //
	//   // Attention Same origin policy, the URL must come from the same origin than
	//   // the web app.
	//   xhr.open('GET', url, true);
	//   // Set the responseType to blob
	//   xhr.responseType = 'blob';
	//   xhr.addEventListener("load", function () {
	//                          if (xhr.status === 200) {
	//                            console.debug("File retrieved");

	//                            // Blob as response
	//                            blob = xhr.response;
	//                            console.debug("Blob:" + blob);

	//                            // Insert the received blob into IndexedDB
	//                            dbAdd(title, year, blob);
	//                          } else {
	//                            console.error("loadConfigFormHtmlContent error:",
	//                                          xhr.responseText, xhr.status);
	//                          }
	//                        }, false);
	//   // Send XHR
	//   xhr.send();
	// }

	  addEventListeners();
	})(); // Immediately-Invoked Function Expression (IIFE)

</script>
</html>